package com.mindskip.xzs.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.mindskip.xzs.base.RestResponse;
import com.mindskip.xzs.component.QywxComponent;
import com.mindskip.xzs.constant.CommonRedisKey;
import com.mindskip.xzs.domain.*;
import com.mindskip.xzs.domain.enums.ExamTypeEnum;
import com.mindskip.xzs.domain.enums.QywxMsgType;
import com.mindskip.xzs.domain.exam.ExamPaperQuestionItemObject;
import com.mindskip.xzs.domain.exam.ExamPaperTitleItemObject;
import com.mindskip.xzs.domain.other.KeyValue;
import com.mindskip.xzs.exception.BusinessException;
import com.mindskip.xzs.repository.ExamMapper;
import com.mindskip.xzs.repository.ExamPaperMapper;
import com.mindskip.xzs.repository.QuestionMapper;
import com.mindskip.xzs.service.ExamPaperService;
import com.mindskip.xzs.service.QuestionService;
import com.mindskip.xzs.service.TextContentService;
import com.mindskip.xzs.service.enums.ActionEnum;
import com.mindskip.xzs.utility.DateTimeUtil;
import com.mindskip.xzs.utility.ExamUtil;
import com.mindskip.xzs.utility.JsonUtil;
import com.mindskip.xzs.utility.ModelMapperSingle;
import com.mindskip.xzs.viewmodel.IdNameKV;
import com.mindskip.xzs.viewmodel.admin.exam.*;
import com.mindskip.xzs.viewmodel.admin.question.QuestionEditRequestVM;
import com.mindskip.xzs.viewmodel.student.dashboard.PaperFilter;
import com.mindskip.xzs.viewmodel.student.dashboard.PaperInfo;
import com.mindskip.xzs.viewmodel.student.exam.ExamPaperPageVM;
import com.mindskip.xzs.viewmodel.student.exampaper.ExamInfoRes;
import com.mindskip.xzs.viewmodel.student.exampaper.PageExamInfoReq;
import com.mindskip.xzs.viewmodel.wx.student.qywx.MarkdownNotice;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@Service
public class ExamPaperServiceImpl extends BaseServiceImpl<ExamPaper> implements ExamPaperService {

    protected final static ModelMapper modelMapper = ModelMapperSingle.Instance();
    private final ExamPaperMapper examPaperMapper;
    private final QuestionMapper questionMapper;
    private final TextContentService textContentService;
    private final QuestionService questionService;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    private ExamMapper examMapper;
    @Resource
    private QywxComponent qywxComponent;

    /**
     * agentid
     * 每个应用都有唯一的agentid。在管理后台->“应用与小程序”->“应用”，点进某个应用，即可看到agentid。
     */
    @Value("${qywx.agentId}")
    private Long agentId;

    @Autowired
    public ExamPaperServiceImpl(ExamPaperMapper examPaperMapper, QuestionMapper questionMapper, TextContentService textContentService, QuestionService questionService) {
        super(examPaperMapper);
        this.examPaperMapper = examPaperMapper;
        this.questionMapper = questionMapper;
        this.textContentService = textContentService;
        this.questionService = questionService;
    }


    @Override
    public PageInfo<ExamPaper> page(ExamPaperPageReq requestVM) {
        return PageHelper.startPage((requestVM == null || requestVM.getPageIndex() == null) ? 1 : requestVM.getPageIndex(), (requestVM == null || requestVM.getPageSize() == null) ? 10 : requestVM.getPageSize(), "id desc").doSelectPageInfo(() ->
                examPaperMapper.page(requestVM));
    }

    @Override
    public PageInfo<ExamPaper> taskExamPage(ExamPaperPageRequestVM requestVM) {
        return PageHelper.startPage(requestVM.getPageIndex(), requestVM.getPageSize(), "id desc").doSelectPageInfo(() ->
                examPaperMapper.taskExamPage(requestVM));
    }

    @Override
    public PageInfo<ExamPaper> studentPage(ExamPaperPageVM requestVM) {
        return PageHelper.startPage(requestVM.getPageIndex(), requestVM.getPageSize(), "id desc").doSelectPageInfo(() ->
                examPaperMapper.studentPage(requestVM));
    }


    @Override
    @Transactional
    public ExamPaper savePaperFromVM(ExamPaperEditRequestVM examPaperEditRequestVM, User user) {
        ActionEnum actionEnum = (examPaperEditRequestVM.getId() == null) ? ActionEnum.ADD : ActionEnum.UPDATE;
        Date now = new Date();
        List<ExamPaperTitleItemVM> titleItemsVM = examPaperEditRequestVM.getTitleItems();
        List<ExamPaperTitleItemObject> frameTextContentList = frameTextContentFromVM(titleItemsVM);
        String frameTextContentStr = JsonUtil.toJsonStr(frameTextContentList);

        ExamPaper examPaper;
        if (actionEnum == ActionEnum.ADD) {
            examPaper = modelMapper.map(examPaperEditRequestVM, ExamPaper.class);
            TextContent frameTextContent = new TextContent(frameTextContentStr, now);
            textContentService.insertByFilter(frameTextContent);
            examPaper.setFrameTextContentId(frameTextContent.getId());
            examPaper.setCreateTime(now);
            examPaper.setCreateUser(user.getId());
            examPaper.setDeleted(false);
            examPaperFromVM(examPaperEditRequestVM, examPaper, titleItemsVM);
            examPaperMapper.insertSelective(examPaper);
        } else {
            examPaper = examPaperMapper.selectByPrimaryKey(examPaperEditRequestVM.getId());
            TextContent frameTextContent = textContentService.selectById(examPaper.getFrameTextContentId());
            frameTextContent.setContent(frameTextContentStr);
            textContentService.updateByIdFilter(frameTextContent);
            modelMapper.map(examPaperEditRequestVM, examPaper);
            examPaperFromVM(examPaperEditRequestVM, examPaper, titleItemsVM);
            examPaperMapper.updateByPrimaryKeySelective(examPaper);
        }
        return examPaper;
    }

    @Override
    public ExamPaperEditRequestVM examPaperToVM(Integer id) {
        ExamPaper examPaper = examPaperMapper.selectByPrimaryKey(id);
        if (examPaper == null) {
            throw new BusinessException("编号为" + id + "的对应试卷不存在");
        }
        ExamPaperEditRequestVM vm = modelMapper.map(examPaper, ExamPaperEditRequestVM.class);
        TextContent frameTextContent = textContentService.selectById(examPaper.getFrameTextContentId());
        List<ExamPaperTitleItemObject> examPaperTitleItemObjects = JsonUtil.toJsonListObject(frameTextContent.getContent(), ExamPaperTitleItemObject.class);
        List<Integer> questionIds = examPaperTitleItemObjects.stream()
                .flatMap(t -> t.getQuestionItems().stream()
                        .map(q -> q.getId()))
                .collect(Collectors.toList());
        List<Question> questions = questionMapper.selectByIds(questionIds);
        List<ExamPaperTitleItemVM> examPaperTitleItemVMS = examPaperTitleItemObjects.stream().map(t -> {
            ExamPaperTitleItemVM tTitleVM = modelMapper.map(t, ExamPaperTitleItemVM.class);
            List<QuestionEditRequestVM> questionItemsVM = t.getQuestionItems().stream().map(i -> {
                Question question = questions.stream().filter(q -> q.getId().equals(i.getId())).findFirst().get();
                QuestionEditRequestVM questionEditRequestVM = questionService.getQuestionEditRequestVM(question);
                questionEditRequestVM.setItemOrder(i.getItemOrder());
                return questionEditRequestVM;
            }).collect(Collectors.toList());
            tTitleVM.setQuestionItems(questionItemsVM);
            return tTitleVM;
        }).collect(Collectors.toList());
        vm.setTitleItems(examPaperTitleItemVMS);
        vm.setScore(ExamUtil.scoreToVM(examPaper.getScore()));
/*        if (ExamTypeEnum.TimeLimit == ExamTypeEnum.fromCode(examPaper.getExamType())) {
            List<String> limitDateTime = Arrays.asList(DateTimeUtil.dateFormat(examPaper.getLimitStartTime()), DateTimeUtil.dateFormat(examPaper.getLimitEndTime()));
            vm.setLimitDateTime(limitDateTime);
        }*/
        return vm;
    }

    @Override
    public RestResponse examPaperToVM(Integer userId, Integer id, Integer examId) {
        Integer count = examMapper.queryExamLimit(userId, examId);
        if (count < 1) {
            return RestResponse.fail(305, "无权限继续考试");
        }
        return RestResponse.ok(examPaperToVM(id));
    }

    @Override
    public List<PaperInfo> indexPaper(PaperFilter paperFilter) {
       // return examPaperMapper.indexPaper(paperFilter);
        List<ExamInfoRes> examInfoRes = examMapper.queryExamInfoList(new PageExamInfoReq().setUserId(paperFilter.getUserId()));
        if (CollectionUtil.isEmpty(examInfoRes)) {
            return Collections.emptyList();
        }
        ArrayList<PaperInfo> paperInfos = new ArrayList<>();
        examInfoRes.forEach(examInfo -> {
            paperInfos.add(
                    new PaperInfo()
                            .setId(examInfo.getPaperId())
                            .setName(examInfo.getExamName())
                            .setLimitStartTime(examInfo.getLimitEndTime())
                            .setLimitEndTime(examInfo.getLimitEndTime())
            );
        });
        return paperInfos;
    }

    @Override
    public PageInfo<ExamInfoRes> indexExam(PageExamInfoReq req) {
        PageHelper.startPage(req.getPageNum(), req.getPageSize());
        List<ExamInfoRes> examInfoList = examMapper.queryExamInfoList(req);
        if (CollectionUtil.isEmpty(examInfoList)) {
            return new PageInfo<ExamInfoRes>();
        }
        examInfoList.forEach(res -> {
            if (res.getPaperScoreInt() != null) {
                res.setPaperScore(ExamUtil.scoreToVM(res.getPaperScoreInt()));
            }
            if (res.getLimitStartTime() != null) {
                res.setLimitStartTimeStr(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分").format(res.getLimitStartTime()));
            } else {
                res.setLimitStartTimeStr(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分").format(res.getPublishTime()));
            }
            if (res.getLimitEndTime() != null) {
                res.setLimitEndTimeStr(DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分").format(res.getLimitEndTime()));
            } else {
                res.setLimitEndTimeStr("长期");
            }
        });
        return new PageInfo<>(examInfoList);
    }

    @Override
    public Integer selectAllCount() {
        return examPaperMapper.selectAllCount();
    }

    @Override
    public List<Integer> selectMothCount() {
        Date startTime = DateTimeUtil.getMonthStartDay();
        Date endTime = DateTimeUtil.getMonthEndDay();
        List<KeyValue> mouthCount = examPaperMapper.selectCountByDate(startTime, endTime);
        List<String> mothStartToNowFormat = DateTimeUtil.MothStartToNowFormat();
        return mothStartToNowFormat.stream().map(md -> {
            KeyValue keyValue = mouthCount.stream().filter(kv -> kv.getName().equals(md)).findAny().orElse(null);
            return null == keyValue ? 0 : keyValue.getValue();
        }).collect(Collectors.toList());
    }

    private void examPaperFromVM(ExamPaperEditRequestVM examPaperEditRequestVM, ExamPaper examPaper, List<ExamPaperTitleItemVM> titleItemsVM) {
       // Integer gradeLevel = subjectService.levelBySubjectId(examPaperEditRequestVM.getSubjectId());
        Integer questionCount = titleItemsVM.stream()
                .mapToInt(t -> t.getQuestionItems().size()).sum();
        Integer score = titleItemsVM.stream().
                flatMapToInt(t -> t.getQuestionItems().stream()
                        .mapToInt(q -> ExamUtil.scoreFromVM(q.getScore()))
                ).sum();
        examPaper.setQuestionCount(questionCount);
        examPaper.setScore(score);
      //  examPaper.setGradeLevel(gradeLevel);
/*        List<String> dateTimes = examPaperEditRequestVM.getLimitDateTime();
        if (ExamTypeEnum.TimeLimit == ExamTypeEnum.fromCode(examPaper.getExamType())) {
            examPaper.setLimitStartTime(DateTimeUtil.parse(dateTimes.get(0), DateTimeUtil.STANDER_FORMAT));
            examPaper.setLimitEndTime(DateTimeUtil.parse(dateTimes.get(1), DateTimeUtil.STANDER_FORMAT));
        }*/
    }

    private List<ExamPaperTitleItemObject> frameTextContentFromVM(List<ExamPaperTitleItemVM> titleItems) {
        AtomicInteger index = new AtomicInteger(1);
        return titleItems.stream().map(t -> {
            ExamPaperTitleItemObject titleItem = modelMapper.map(t, ExamPaperTitleItemObject.class);
            List<ExamPaperQuestionItemObject> questionItems = t.getQuestionItems().stream()
                    .map(q -> {
                        ExamPaperQuestionItemObject examPaperQuestionItemObject = modelMapper.map(q, ExamPaperQuestionItemObject.class);
                        examPaperQuestionItemObject.setItemOrder(index.getAndIncrement());
                        return examPaperQuestionItemObject;
                    })
                    .collect(Collectors.toList());
            titleItem.setQuestionItems(questionItems);
            return titleItem;
        }).collect(Collectors.toList());
    }


    @Override
    public List<IdNameKV> queryPaperKVListInCache(String keyword) {
        List<IdNameKV> paperKVResList = (List<IdNameKV>) redisTemplate.opsForValue().get(CommonRedisKey.ALL_PAPER_NAME_KV_KEY);
        if (StringUtils.isNotBlank(keyword) && paperKVResList != null) {
            paperKVResList = paperKVResList.stream().filter(paperKVRes -> paperKVRes.getName().contains(keyword)).collect(Collectors.toList());
        }
        return paperKVResList;
    }

    /**
     * 更新缓存
     */
    @Override
    public void updatePaperKVInCache() {
        List<IdNameKV> examKVList = examPaperMapper.queryPaperKVList();
        if (CollectionUtil.isNotEmpty(examKVList)) {
            redisTemplate.opsForValue().set(CommonRedisKey.ALL_PAPER_NAME_KV_KEY, examKVList, 30, TimeUnit.DAYS);
        }
    }


    /**
     * 通知排名
     */
    @Override
    public Boolean noticeExamRank(Integer examId) {
        List<ExamRankInfoVM> examRankInfos = examMapper.queryExamRankNoticeInfo(examId);
        if (CollectionUtil.isEmpty(examRankInfos)) {
            throw new BusinessException("该考试没有参考员工");
        }
        // 多次考试记录处理。
        Map<String, List<ExamRankInfoVM>> groupByQywxMap = examRankInfos.stream().collect(Collectors.groupingBy(ExamRankInfoVM::getQywxId));
        List<ExamRankInfoVM> distinctRecords = new ArrayList<>();
        groupByQywxMap.forEach((qywx, list) -> {
            // 取最高分 -> 分数相等取最新
            Optional<ExamRankInfoVM> first = list.stream().max(Comparator.comparing(ExamRankInfoVM::getUserScore).thenComparing(ExamRankInfoVM::getSubmitTime));
            first.ifPresent(distinctRecords::add);
        });
        List<ExamRankInfoVM> rankInfos = distinctRecords.stream().sorted(Comparator.comparing(ExamRankInfoVM::getUserScore).reversed()).collect(Collectors.toList());
        int rank = 1;
        for (ExamRankInfoVM examRankInfo : rankInfos) {
            qywxComponent.sendMsg(
                    new MarkdownNotice()
                            .setTouser(examRankInfo.getQywxId())
                            .setMsgtype(QywxMsgType.MARK_DOWN.getMsgtype())
                            .setAgentid(agentId)
                            .setEnable_duplicate_check(0)
                            .setDuplicate_check_interval(1800)
                            .setMarkdown(
                                    new MarkdownNotice.Notice()
                                            .setContent(
                                                    String.format(
                                                            "%s的成绩单\n" +
                                                                    ">考试名称：**%s** \n" +
                                                                    ">总分：%s\n" +
                                                                    ">得分：<font color=\"comment\">%s</font> \n" +
                                                                    ">排名：`%s`\n" +
                                                                    "> \n" +
                                                                    ">考试日期：<font color=\"warning\">%s</font> \n"
                                                            , examRankInfo.getUserName()
                                                            , examRankInfo.getExamName()
                                                            , ExamUtil.scoreToVM(examRankInfo.getPaperScore())
                                                            , ExamUtil.scoreToVM(examRankInfo.getUserScore())
                                                            , rank
                                                            , examRankInfo.getSubmitTime().format(DateTimeFormatter.ofPattern("MM月dd日 HH:mm"))
                                                    )
                                            )
                            )
            );
            rank++;
        }
        return null;
    }
}
